use std::{
    env,
    sync::{
        atomic::{AtomicUsize, Ordering},
        Arc,
    },
    time::Instant,
};

use actix::Addr;
use actix_cors::Cors;
use actix_web::{
    get, http, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, Responder,
};
use ctrl::score_ctrl::router as score_router;
use dotenv::dotenv;
use rs::client::sub_channel_notice;
use tokio::task::{spawn, spawn_local};
use ws::{
    handler,
    server::{ChatServer, ChatServerHandle},
};

struct AppState {
    app_name: String,
}

pub mod ctrl {
    pub mod score_ctrl;
}

pub mod models {
    pub mod response;
    pub mod score;
}

pub mod rs {
    pub mod client;
}

pub mod ws {
    pub mod handler;
    pub mod resp;
    pub mod server;
}

#[get("/")]
async fn index(data: web::Data<AppState>) -> String {
    let app_name = &data.app_name; // <- get app_name
    format!("Client: {app_name}!") // <- response with app_name
}

/// websocket 入口
async fn notice_ws(
    req: HttpRequest,
    stream: web::Payload,
    chat_server: web::Data<ChatServerHandle>,
) -> Result<HttpResponse, Error> {
    let (res, session, msg_stream) = actix_ws::handle(&req, stream)?;

    // 生成websocket处理程序（无需等待），立即返回响应
    spawn_local(handler::chat_ws(
        (**chat_server).clone(),
        session,
        msg_stream,
    ));

    Ok(res)
}

#[tokio::main(flavor = "current_thread")]
async fn main() -> std::io::Result<()> {
    dotenv().ok();

    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));

    // 配置文件
    let ws_path = env::var("WS_PATH").unwrap();
    let rs_url = env::var("RS_URL").unwrap();

    // redis 与 ws 初始化
    let redis = redis::Client::open(rs_url).unwrap();
    let (chat_server, server_tx) = ChatServer::new();

    // 线程启动 ws
    spawn(chat_server.run());

    // redis 订阅 channel_notice 通道
    sub_channel_notice(
        web::Data::new(redis.clone()),
        web::Data::new(server_tx.clone()),
    );

    log::info!("订阅服务启动: http://localhost:8085");

    HttpServer::new(move || {
        // cors
        let cors = Cors::default()
            .allow_any_origin()
            .allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
            .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
            .allowed_header(http::header::CONTENT_TYPE)
            .max_age(3600);

        App::new()
            .app_data(web::Data::new(server_tx.clone()))
            .app_data(web::Data::new(redis.clone()))
            .app_data(web::Data::new(AppState {
                app_name: String::from("n-app"),
            }))
            .wrap(cors)
            .service(web::resource(&ws_path).route(web::get().to(notice_ws)))
            .service(index)
            .configure(score_router)
            .wrap(middleware::Logger::default())
    })
    .workers(2)
    .bind(("0.0.0.0", 8085))?
    .run()
    .await
}
